//
// Created by gastonfeng on 2022/12/19.
//
#ifdef USE_AWTK

#include "base/main_loop.h"
#include "tkc/mem.h"
#include "base/pixel.h"
#include "TFT_eSPI.h"
#include "lcd/lcd_mem_fragment.h"
#include "widgets/calibration_win.h"
#include "lcd_awtk.h"
#include "plc_rte.h"

#include "tkc/fs.h"
#include "tkc/utils.h"
#include "tkc/str_str.h"
#include "gui_type.h"
#include "base/window.h"
#include "widgets/button.h"
#include "widgets/label.h"
#include "rte_data.h"

#include "rtos.h"

using pixel_t = uint16_t;
extern TFT_eSPI tft;
#define LCD_FORMAT BITMAP_FMT_BGR565
#define pixel_from_rgb(r, g, b) \
    ((((b) >> 3) << 11) | (((g) >> 2) << 5) | ((r) >> 3))
#define pixel_to_rgba(p)                                                                    \
    {                                                                                       \
        (0xff & ((p >> 3) << 3)), (0xff & ((p >> 5) << 2)), (0xff & ((p >> 11) << 3)), 0xff \
    }

#define set_window_func tft.setWindow
// #define write_data_func tft.pushColor
// #define pixel_src_t pixel_rgb565_t
// #define pixel_src_format pixel_rgb565_format
#define pixel_from_rgba(r, g, b, a) pixel_from_rgb(r, g, b)
// #define pixel_src_to_rgba pixel_rgb565_to_rgba

ret_t application_init() {
    return RET_OK;
}

ret_t application_exit() {
    logger.debug("application_exit\n");
    return RET_OK;
}

extern "C"
{
#include "assets.inc"
}

#include "awtk_main.inc"

ret_t platform_disaptch_input(main_loop_t *l) {
    return RET_OK;
}

static lcd_t *platform_create_lcd(wh_t w, wh_t h) {
    return lcd_mem_fragment_create(w, h);
}

#include "blend/pixel_ops.inc"
#include "lcd/lcd_mem_fragment.inc"
#include "main_loop/main_loop_raw.inc"
#include "keyboard/keyboard.h"
#include "hwboard.h"

void dispatch_input_events() {
    //    int key = KEY_Scan(0);
    //
    //    switch (key) {
    //        case KEY_UP: {
    //            key = TK_KEY_UP;
    //            break;
    //        }
    //        case KEY_DOWN: {
    //            key = TK_KEY_DOWN;
    //            break;
    //        }
    //        case KEY_LEFT: {
    //            key = TK_KEY_RETURN;
    //            break;
    //        }
    //        case KEY_RIGHT: {
    //            key = TK_KEY_BACK;
    //            break;
    //        }
    //        default: { key = 0; }
    //    }
    //
    //    if (key) {
    //        main_loop_post_key_event(main_loop(), TRUE, key);
    //    } else {
    //        main_loop_post_key_event(main_loop(), FALSE, key);
    //    }

    //    if (TOUCH_Scan() == 0) {
    //        main_loop_post_pointer_event(main_loop(), TRUE, TouchData.lcdx,
    //                                     TouchData.lcdy);
    //    } else {
    //        main_loop_post_pointer_event(main_loop(), FALSE, TouchData.lcdx,
    //                                     TouchData.lcdy);
    //    }
}

ret_t platform_prepare(void) {
    static bool_t inited = FALSE;
    //    static uint32_t s_heam_mem[4000];

    if (!inited) {
        inited = TRUE;
        //        tk_mem_init(s_heam_mem, sizeof(s_heam_mem));
    }

    return RET_OK;
}

uint64_t get_time_ms64() {
    return Rtos::current_us();
}

void sleep_ms(uint32_t ms) {
    Rtos::Delay(ms);
}

#define FS_REG_MAX 6

static str_str_t s_items[FS_REG_MAX + 1];

static const char *str_str_value_with_len(const str_str_t *items, const char *name) {
    const str_str_t *iter = items;
    uint32_t len;
    return_value_if_fail(items != nullptr && name != nullptr, NULL);

    while (iter->name != nullptr) {
        len = tk_strlen(iter->name);
        if (tk_str_eq_with_len(iter->name, name, len)) {
            if (name[len] == '/' || name[len] == '\0') {
                return iter->value;
            }
        }

        iter++;
    }

    return nullptr;
}

extern "C" widget_t *keypad_create(widget_t *parent, xy_t x, xy_t y, wh_t w, wh_t h);

int lcd_awtk::begin(uint32_t tick) {
    data.height = 240;
    data.width = 320;
    data.step = 0;
    gui_app_start(data.width, data.height);
    widget_factory_register(widget_factory(), "keyboard", keyboard_create);
    //    widget_t *key_win = keypad_create(NULL, 0, 0, 320, 240);
    //    widget_set_style_color(key_win, "bg_color", 0xff00ffff);
    // widget_set_name(key_win, "kb_uint");
    // widget_set_visible(key_win, FALSE);
    board.backlight(1);

    return 0;
}

static ret_t on_show_dialog(void *ctx, event_t *e) {
    uint32_t code = 0;
    widget_t *ok = nullptr;
    widget_t *label = nullptr;
    widget_t *cancel = nullptr;
    widget_t *win = dialog_create_simple(nullptr, 0, 0, 240, 160);
    dialog_t *dialog = DIALOG(win);

    widget_set_text(dialog->title, L"Dialog");

    ok = button_create(dialog->client, 20, 80, 80, 30);
    widget_set_text(ok, L"Go");

    cancel = button_create(dialog->client, 140, 80, 80, 30);
    widget_set_text(cancel, L"Cancel");

    label = label_create(dialog->client, 10, 30, 200, 30);
    widget_set_text(label, L"Are you ready!");

    //    widget_on(ok, EVT_CLICK, on_ok, dialog);
    //    widget_on(cancel, EVT_CLICK, on_cancel, dialog);

    code = dialog_modal(win);
    log_debug("code=%d\n", (int) code);

    (void) code;
    tk_mem_dump();
    (void) e;

    return RET_OK;
}

#ifdef __PLATFORMIO_BUILD_DEBUG__
int lcd_awtk::dev_test(uint32_t tick)
{
    return 0;
}
#endif

int lcd_awtk::diag(uint32_t tick) {
    return 0;
}

void hmi_set_int(_widget_t *widget, int v) {
    if (awtk.data.current == awtk.current_win)
        if (tk_str_eq(widget->state, WIDGET_STATE_NORMAL) || tk_str_eq(widget->state, WIDGET_STATE_EMPTY))
            edit_set_int(widget, v);
}

void set_text_color(_widget_t *widget, uint32_t color) {
    if (awtk.data.current == awtk.current_win)
        widget_set_style_color(widget, "text_color", color);
}

int hmi_get_int(_widget_t *widget) {
    if (awtk.data.current == awtk.current_win)
        return edit_get_int(widget);
    return 0;
}

void hmi_set_text(_widget_t *widget, const char *v) {
    if (awtk.data.current == awtk.current_win)
        if (tk_str_eq(widget->state, WIDGET_STATE_NORMAL) || tk_str_eq(widget->state, WIDGET_STATE_EMPTY))
            widget_set_text_utf8(widget, v);
}

ret_t on_action(void *ctx, event_t *evt) {
    if (awtk.data.current == awtk.current_win) {
        nWidget_t *widget = (nWidget_t *) ctx;
        input_method_t *im = input_method();
        widget_t *edit = (widget_t *) im->keyboard->children->elms[0];
        int val = edit_get_int(edit);
        if (widget->input != nullptr)
            widget->input(val);
        edit_set_focus(widget->handle, FALSE);
        //    input_method_request(im, NULL);
    }
    return RET_OK;
}

_widget_t *lcd_awtk::widgetCreate(widget_t *pWidget, nWidget_t *widget) {
    switch (widget->type) {
        case WIDGET_BUTTON: {
            widget->handle = button_create(pWidget, widget->x, widget->y, widget->w, widget->h);
            widget_set_style_color(widget->handle, "bg_color", widget->color);
            widget_set_style_color(widget->handle, "text_color", 0xff000000);
            widget_set_text_utf8(widget->handle, widget->text);
            if (widget->on_button_click != nullptr) {
                widget_on(widget->handle, EVT_POINTER_DOWN, (event_func_t) widget->on_button_click, nullptr);
            }
            if (widget->on_button_up != nullptr) {
                widget_on(widget->handle, EVT_POINTER_UP, (event_func_t) widget->on_button_up, nullptr);
            }
            // button_set_enable_preview(widget->handle, TRUE);
            return widget->handle;
        }
        case WIDGET_LABEL: {
            widget->handle = label_create(pWidget, widget->x, widget->y, widget->w, widget->h);
            widget_set_text_utf8(widget->handle, widget->text);
            widget_set_style_color(widget->handle, "bg_color", widget->color);
            return widget->handle;
        }
        case WIDGET_EDIT: {
            widget->handle = edit_create(pWidget, widget->x, widget->y, widget->w, widget->h);
            if (widget->text)
                widget_set_text_utf8(widget->handle, widget->text);
            widget_set_style_color(widget->handle, "bg_color", widget->color);
            if (widget->input) {
                edit_set_input_type(widget->handle, INPUT_UINT);
                edit_set_action_text(widget->handle, "OK");
                widget_on(widget->handle, EVT_IM_ACTION, on_action, widget);
            } else {
                edit_set_readonly(widget->handle, TRUE);
            }
            return widget->handle;
        }
        default: {
            return nullptr;
        }
    }
    return nullptr;
}

lcd_awtk awtk;

void calibration_win_save(int win);

static ret_t ui_on_btn_feedback(void *ctx, widget_t *widget, event_t *evt) {
    //   widget_t* win = (widget_t*)ctx;
    //   widget_t* label = widget_lookup(win, "label", TRUE);
    //   const char* name = widget_get_prop_str(widget, WIDGET_PROP_NAME, "");

    //   if (tk_str_eq(name, "button")) {
    if (evt->type == EVT_POINTER_DOWN) {
        //   widget_set_text_utf8(label, "point down");
        board.beep(1);
    } else if (evt->type == EVT_POINTER_UP) {
        //   widget_set_text_utf8(label, "point up");
        board.beep(0);
    }
    //   }
    return RET_OK;
}

int lcd_awtk::run(uint32_t tick) {
    switch (data.step) {

        case 0:
            if (rte.event(pb_event_TOUCH) == 1) {
                rte_config.caled = 0;
            } else {
                if (rte_config.caled) {
                    data.step = 2;
                } else {
                    touch_calibrate();
                    data.step = 1;
                }
            }
            break;
        case 1:
            if (rte_config.caled) {
                data.step = 2;
            }
            break;
        case 2:
            if (dat)
                data.step++;
            break;
        case 3: {
            if (data.current != current_win)
                data.step++;
        }
            break;
        case 4: {
            if (win) {
                widget_destroy(win->handle);
                win = nullptr;
            }
            data.step++;
        }
            break;
        case 5: {
            win = dat->win[data.current];
            win->handle = window_create(nullptr, 0, 0, win->w, win->h);
            char c[2];
            c[0] = 'A' + data.current;
            c[1] = 0;
            widget_set_name(win->handle, c);
            // widget_set_style_color(win->handle, "default.bg_color", win->color);
            widget_set_style_color(win->handle, "bg_color", win->color);
            for (int j = 0; j < win->widget_nrs; j++) {
                nWidget_t *widget = win->widgets[j];
                widget->handle = widgetCreate(win->handle, widget);
            }
            // ui_feedback_init(ui_on_btn_feedback, win->handle);
            data.step++;
        }
            break;
        case 6:
            current_win = data.current;
            data.step++;
            break;
        case 7:
            data.step = 3;
            break;
        default:
            break;
    }
    main_loop_t *l = main_loop();
    return_value_if_fail(l != nullptr && l->run != nullptr, RET_BAD_PARAMS);
    ++l->running;
    main_loop_simple_t *loop = (main_loop_simple_t *) l;

    loop->pressed = FALSE;
    if (l->running) {
        if (l->quit_num) {
            --l->quit_num;
            --l->running;
        } else
            main_loop_step(l);
    }
    dispatch_input_events();
    return 0;
}

#define CAL_ERROR 50

ret_t calibration_done(void *ctx, point_t *e) {
    for (int i = 0; i < PT_MAX_NR; i++) {
        if ((e[i].x == 0) || (e[i].y == 0)) {
            return RET_BAD_PARAMS;
        }
    }
    // if (abs(e[0].x - e[3].x) > CAL_ERROR || abs(e[0].y - e[1].y) > CAL_ERROR)
    // {
    //     return RET_BAD_PARAMS;
    // }
    // if (abs(e[1].x - e[2].x) > CAL_ERROR || abs(e[2].y - e[3].y) > CAL_ERROR)
    // {
    //     return RET_BAD_PARAMS;
    // }
    int x = (e[0].x + e[3].x) / 2;
    int y = (e[0].y + e[1].y) / 2;
    int x1 = (e[1].x + e[2].x) / 2;
    int y1 = (e[2].y + e[3].y) / 2;
    int screen_center_x = (x + x1) / 2;
    int screen_center_y = (y + y1) / 2;
    if ((abs(e[4].x - screen_center_x) > CAL_ERROR) || (abs(e[4].y - screen_center_y) > CAL_ERROR)) {
        return RET_BAD_PARAMS;
    }
    float xScale = ((float) awtk.data.width - 40) / (((e[1].x - e[0].x) + (e[2].x - e[3].x)) / 2);

    float xBias =
            (20 - (e[0].x + e[3].x) / 2 * xScale + ((float) awtk.data.width - 20) - (e[1].x + e[2].x) / 2 * xScale) / 2;

    float yScale = ((float) awtk.data.height - 40) / (((e[3].y - e[0].y) + (e[2].y - e[1].y)) / 2);

    float yBias =
            (20 - (e[0].y + e[1].y) / 2 * yScale + ((float) awtk.data.height - 20) - (e[2].y + e[3].y) / 2 * yScale) /
            2;

    rte_config.xScale = xScale;
    rte_config.xBias = xBias;
    rte_config.yScale = yScale;
    rte_config.yBias = yBias;
    rte_config.caled = true;
    rte.event(pb_event_TOUCH_CALIBRATED, true);
    return RET_OK;
}

widget_t *calibration_win;

int lcd_awtk::touch_calibrate() {
    calibration_win = calibration_win_create(nullptr, 0, 0, 320, 240);
    calibration_win_set_on_done(calibration_win, calibration_done, nullptr);
    return 0;
}

void lcd_awtk::hmi_set_win(int winitem) {
    awtk.data.current = winitem;
    awtk.dat->current_window = winitem;
}

void set_button_cb_sync(nWidget_t param, void *pVoid) {
    widget_t *widget = (widget_t *) param.handle;
    if (awtk.data.current == awtk.current_win)
        widget_on(widget, EVT_POINTER_DOWN, (event_func_t) pVoid, NULL);
}

void set_button_type(_widget_t *param, int type) {
    widget_t *widget = (widget_t *) param;
    if (awtk.data.current == awtk.current_win) {
        button_set_keep_pressed(widget, type);
        if (type == 1) {
            widget_set_style_color(widget, "bg_color", 0xff00ffff);
        }
    }
}

void set_edit_limit(_widget_t *pWidget, int _max, int _min) {
    widget_t *widget = (widget_t *) pWidget;
    if (awtk.data.current == awtk.current_win)
        edit_set_int_limit(widget, _max, _min, 1);
}

unsigned char lcd_awtk::hmi_get_button_value(_widget_t *pWidget) {
    button_t *button = BUTTON(pWidget);
    if (awtk.data.current == awtk.current_win) return button->pressed;
    return 0;
}

void lcd_awtk::hmi_set_button_value(_widget_t *pWidget, unsigned char i) {
    button_t *button = BUTTON(pWidget);
    if (awtk.data.current == awtk.current_win) {
        if (button->pressed != i) {
            button->pressed = i;
            rect_t r;
            r.x = pWidget->x;
            r.y = pWidget->y;
            r.w = pWidget->w;
            r.h = pWidget->h;
            if (button->pressed)
                widget_set_state(pWidget, WIDGET_STATE_PRESSED);
            else
                widget_set_state(pWidget, WIDGET_STATE_NORMAL);
        }
    }
}

int lcd_awtk::loadui(void *ui_dat) {
    awtk.dat = (nGui_t *) ui_dat;
    awtk.dat->set_int = hmi_set_int;
    awtk.dat->set_text = hmi_set_text;
    awtk.dat->set_button_cb_sync = set_button_cb_sync;
    awtk.dat->set_button_type = set_button_type;
    awtk.dat->set_edit_limit = set_edit_limit;
    awtk.dat->get_int = hmi_get_int;
    awtk.dat->get_button_value = hmi_get_button_value;
    awtk.dat->set_button_value = hmi_set_button_value;
    awtk.dat->set_text_color = set_text_color;
    return 0;
}

void lcd_awtk::hmi_add_assert(const void *str) {
    assets_manager_t *am = assets_manager();
    assets_manager_add(am, str);
    tk_init_assets();
}

#endif
